//
//  DispatchQueue.swift
//  YuxiaorReborn
//
//  Created by GongXiang on 10/18/16.
//  Copyright © 2016 Yuxiaor. All rights reserved.
//

import Foundation

func delay(_ delay: Double, closure: @escaping () -> Void) {
    DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
        closure()
    }
}

typealias DispatchTask = (_ cancel: Bool) -> Void

extension DispatchQueue {
    static var userInteractive: DispatchQueue { return DispatchQueue.global(qos: .userInteractive) }
    static var userInitiated: DispatchQueue { return DispatchQueue.global(qos: .userInitiated) }
    static var utility: DispatchQueue { return DispatchQueue.global(qos: .utility) }
    static var background: DispatchQueue { return DispatchQueue.global(qos: .background) }

    func syncResult<T>(_ closure: () -> T) -> T {
        var result: T!
        sync { result = closure() }
        return result
    }

    private static var _onceTracker = [String]()

    /**
     Executes a block of code, associated with a unique token, only once.  The code is thread safe and will
     only execute the code once even in the presence of multithreaded calls.

     - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
     - parameter block: Block to execute once
     */
    public class func once(token: String, block: () -> Void) {
        objc_sync_enter(self)
        defer { objc_sync_exit(self) }

        if _onceTracker.contains(token) {
            return
        }

        _onceTracker.append(token)

        block()
    }

    @discardableResult
    func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) -> DispatchTask? {
        var closure = Optional(closure)
        var result: DispatchTask?

        let delayClosure: DispatchTask = {
            if let internalClosure = closure {
                if !$0 {
                    self.async { internalClosure() }
                }
            }

            closure = nil
            result = nil
        }

        result = delayClosure

        asyncAfter(deadline: .now() + delay) {
            if let closure = result {
                closure(false)
            }
        }

        return result
    }

    func cancel(task: DispatchTask?) {
        task?(true)
    }
}
